﻿using Autofac;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace HproseRpcTester.Utils
{
    /// <summary>
    /// Autofac依赖注入示例：
    /*          AutofacHelper
                .StartDI()
                .AutofacDependencyInjection(new Dictionary<string, string>
                {
                    { "xhh.ddPCR.ViewModel", "ViewModel"},
                    //{ "xhh.ddPCR.Service", "Service"},
                    //{ "xhh.ddPCR.Model", "Model"}
                }, DependencyInjectionType.Singleton)
                .EndDI();
                //使用示例：//推荐用using(var scope = AutofacHelper.Container.BeginLifetimeScope())
                //var t = AutofacHelper.Container.BeginLifetimeScope().Resolve<xhh.ddPCR.ViewModel.IT2ViewModel>().T();//一次性生命周期
                var t = AutofacHelper.Container.Resolve<xhh.ddPCR.ViewModel.IDetViewModel>();//单例
                var o = t.Pf();
    */
    /// </summary>
    public static class AutofacHelper
    {
        private static List<InstanceExsist> infos = new List<InstanceExsist>();
        private static Dictionary<string, string> registDic = new Dictionary<string, string>();
        private static ContainerBuilder builder;
        public static IContainer Container;
        /// <summary>
        /// true表示已经完成依赖注入
        /// </summary>
        public static bool Valid => Container != null;
        /// <summary>
        /// 开始注入依赖
        /// </summary>
        /// <returns></returns>
        public static ContainerBuilder StartDI() => builder = new ContainerBuilder();
        /// <summary>
        /// 注入依赖到Autofac
        /// </summary>
        /// <param name="assemblyName">包名</param>
        /// <param name="tag">类型后缀</param>
        public static ContainerBuilder AutofacDependencyInjection(this ContainerBuilder bd, string assemblyName, string tag, DependencyInjectionType type)
        {
            //DI
            if (type == DependencyInjectionType.Scoped)
                bd.RegisterAssemblyTypes(Assembly.Load(assemblyName)).Where(x => x.Name.EndsWith(tag)).AsImplementedInterfaces();
            else
                registDic.Add(assemblyName, tag);

            return bd;
        }

        public static ContainerBuilder RegisterSingleton(this ContainerBuilder bd, object instance)
        {
            bd.RegisterInstance(instance).AsImplementedInterfaces();
            AddRPCInstance(instance.GetType(), instance);
            return bd;
        }
        public static ContainerBuilder RegisterSingleton(object instance)
        {
            return builder.RegisterSingleton(instance);
        }


        public static ContainerBuilder RegisterScope(this ContainerBuilder bd, Type scope)
        {
            bd.RegisterType(scope).AsImplementedInterfaces();
            return bd;
        }

        private static bool CreateInstanceEx(ref List<InstanceInfo> ins)
        {
            if (ins.All(x => x.Result != null)) return true;
            else
            {
                var over = new List<InstanceInfo>();
                over.AddRange(ins.Where(x => x.Result != null));
                var none = ins.Except(over).ToList();
                for (int i = none.Count - 1; i > -1; i--)
                {
                    if (none[i].Parameters.Length == 0)
                        none[i].Result = Activator.CreateInstance(none[i].Instance, new object[] { });
                    else
                    {
                        List<object> paras = new List<object>();
                        bool hasPara = true;
                        foreach (var p in none[i].Parameters)
                        {
                            var index = over.FindIndex(x => x.Instance == p.ParameterType || x.Implements.Any(y => y == p.ParameterType));
                            //参数p未创建
                            if (index == -1)
                            {
                                index = infos.FindIndex(x => x.Instance == p.ParameterType || x.Instance.GetInterface(p.ParameterType.Name) != null);
                                if (index == -1)
                                {
                                    hasPara = false;
                                    break;
                                }
                                else paras.Add(infos[index].Result);
                            }
                            else //参数p已创建
                                paras.Add(over[index].Result);
                        }
                        if (!hasPara) continue;
                        //所有参数都已经创建
                        none[i].Result = Activator.CreateInstance(none[i].Instance, paras.ToArray());
                    }
                    over.Add(none[i]);
                    none.RemoveAt(i);
                }
            }
            return CreateInstanceEx(ref ins);
        }

        internal static void AddRPCInstance(Type instanceType, object instance)
        {
            infos.Add(new InstanceExsist(instanceType) { Result = instance });
        }

        /// <summary>
        /// 注入依赖到Autofac
        /// </summary>
        /// <param name="keyValuePairs">需要注入的数组</param>
        public static ContainerBuilder AutofacDependencyInjection(this ContainerBuilder bd, IEnumerable<KeyValuePair<string, string>> keyValuePairs, DependencyInjectionType type)
        {
            if (type == DependencyInjectionType.Scoped)
            {
                foreach (var item in keyValuePairs)
                    bd.RegisterAssemblyTypes(Assembly.Load(item.Key)).Where(x => x.Name.EndsWith(item.Value)).AsImplementedInterfaces();
            }
            else
            {
                foreach (var item in keyValuePairs)
                    bd.AutofacDependencyInjection(item.Key, item.Value, DependencyInjectionType.Singleton);
            }
            return builder;
        }

        /// <summary>
        /// 注入完依赖后必须调用
        /// </summary>
        /// <param name="bd"></param>
        public static IContainer EndDI(this ContainerBuilder bd)
        {
            var ins = new List<InstanceInfo>();
            foreach (var sub in registDic)
            {
                var assemblyName = sub.Key;
                var tag = sub.Value;
                //获取所有要注入的类
                var t = GetClassName(assemblyName, tag);
                //获取要注入的类的数据
                foreach (var item in t)
                {
                    var con = item.Key.GetConstructors();
                    ConstructorInfo c = con.FirstOrDefault();
                    var l = c?.GetParameters().Length ?? 0;
                    foreach (var i in con)
                    {
                        if (i.GetParameters().Length < l)
                            c = i;
                    }
                    var paras = c?.GetParameters() ?? new ParameterInfo[0];
                    ins.Add(new InstanceInfo(c, item.Value, item.Key, paras));
                }
            }

            //创建类的实例
            if (ins.Count > 0)
                CreateInstanceEx(ref ins);
            //DI
            foreach (var item in ins)
            {
                if (item.Implements.Length > 0)
                    bd.RegisterInstance(item.Result).AsImplementedInterfaces();
                else bd.RegisterInstance(item.Result);
            }

            registDic.Clear();
            Container = bd.Build();
            return Container;
        }

        public static IContainer EndDI()
        {
            return builder.EndDI();
        }

        /// <summary>  
        /// 获取程序集中的实现类对应的多个接口
        /// </summary>  
        /// <param name="assemblyName">程序集</param>
        private static IDictionary<Type, Type[]> GetClassName(this string assemblyName, string tag)
        {
            if (!String.IsNullOrEmpty(assemblyName))
            {
                Assembly assembly = Assembly.Load(assemblyName);
                List<Type> ts = assembly.GetTypes().ToList();

                var result = new Dictionary<Type, Type[]>();
                foreach (var item in ts.Where(s => (!s.IsInterface && s.Name.IndexOf(tag) > 0)))
                {
                    var interfaceType = item.GetInterfaces();
                    result.Add(item, interfaceType);
                }
                return result;
            }
            return new Dictionary<Type, Type[]>();
        }
    }

    public enum DependencyInjectionType
    {
        Scoped,
        Singleton
    }

    internal class InstanceInfo : InstanceExsist
    {
        public InstanceInfo(ConstructorInfo constructor, Type[] implements, Type instance, ParameterInfo[] parameters) : base(instance)
        {
            Constructor = constructor;
            Implements = implements;
            Parameters = parameters;
        }

        public ConstructorInfo Constructor { get; set; }
        public Type[] Implements { get; set; }
        public ParameterInfo[] Parameters { get; set; }
        public override bool Equals(object obj)
        {
            if (null == obj) return false;
            if (!(obj is InstanceInfo)) return false;
            return Instance.Equals((obj as InstanceInfo).Instance);
        }

        public override int GetHashCode()
        {
            return 1572474754 + EqualityComparer<Type>.Default.GetHashCode(Instance);
        }

        public override string ToString()
        {
            return $"类：{Instance.Name} 参数：{string.Join(",", Parameters.Select(x => $"{x.ParameterType.Name} {x.Name}").ToArray())}";
        }
    }

    internal class InstanceExsist
    {
        public InstanceExsist(Type instance)
        {
            Instance = instance;
        }

        public Type Instance { get; set; }
        public object Result { get; set; } = null;

        public override string ToString()
        {
            return $"{Instance}";
        }
    }
}
